APIs & Integrations
Spatial
Structr provides support for geographic data. This includes a built-in Location type with distance-based queries, geocoding to convert addresses to coordinates, geometry processing for polygons and spatial analysis, and import capabilities for standard geospatial file formats.
Note: The geometry functions require the
geo-transformationsmodule.
The Location Type
Structr includes a built-in Location type for storing geographic coordinates. This type has two key properties:
| Property | Type | Description |
|---|---|---|
latitude |
Double | Latitude coordinate (WGS84) |
longitude |
Double | Longitude coordinate (WGS84) |
Creating Locations
Create Location objects like any other Structr type:
{
// Create a location for Frankfurt
let frankfurt = $.create('Location', {
name: 'Frankfurt Office',
latitude: 50.1109,
longitude: 8.6821
});
}
You can also extend the Location type or add these properties to your own types. Any type with latitude and longitude properties can use distance-based queries.
Distance-Based Queries
The withinDistance predicate finds objects within a specified radius of a point. The distance is measured in kilometers.
{
// Find all locations within 25 km of a point
let nearbyLocations = $.find('Location', $.withinDistance(50.1109, 8.6821, 25));
$.log('Found ' + $.size(nearbyLocations) + ' locations');
}
This works with any type that has latitude and longitude properties:
{
// Find stores within 10 km
let nearbyStores = $.find('Store', $.withinDistance(customerLat, customerLon, 10));
// Find events within 50 km
let nearbyEvents = $.find('Event', $.withinDistance(userLat, userLon, 50));
}
Distance Queries via REST API
The REST API supports distance-based queries using request parameters. Any type with latitude and longitude properties (typically by extending the built-in Location type) can be queried this way.
Using coordinates directly:
curl "http://localhost:8082/structr/rest/Hotel?_latlon=50.1167851,8.7265218&_distance=0.1"
The _latlon parameter specifies the search origin as latitude,longitude, and _distance specifies the search radius in kilometers.
Using address components:
curl "http://localhost:8082/structr/rest/Store?_country=Germany&_city=Frankfurt&_street=Hauptstraße&_distance=5"
Using combined location string:
curl "http://localhost:8082/structr/rest/Restaurant?_location=Germany,Berlin,Unter%20den%20Linden&_distance=2"
The _location parameter accepts the format country,city,street.
Request Parameters for Distance Search:
| Parameter | Description |
|---|---|
_latlon |
Search origin as latitude,longitude |
_distance |
Search radius in kilometers |
_location |
Search origin as country,city,street |
_country |
Country (used with other address fields) |
_city |
City (used with other address fields) |
_street |
Street (used with other address fields) |
_postalCode |
Postal code (used with other address fields) |
When using address-based parameters (_location or the individual fields), Structr geocodes the address using the configured provider and searches for objects within the specified radius. Geocoded addresses are cached to minimize API calls.
Geocoding
Geocoding converts addresses into geographic coordinates. Structr uses geocoding automatically when you use the distance parameter in REST queries.
Configuration
Configure geocoding in the Configuration Interface:
| Setting | Description |
|---|---|
geocoding.provider |
Full class name of the provider |
geocoding.apikey |
API key (required for Google and Bing) |
geocoding.language |
Language for results (e.g., en, de) |
Supported Providers
| Provider | Class Name | API Key |
|---|---|---|
| Google Maps | org.structr.common.geo.GoogleGeoCodingProvider |
Required |
| Bing Maps | org.structr.common.geo.BingGeoCodingProvider |
Required |
| OpenStreetMap | org.structr.common.geo.OSMGeoCodingProvider |
Not required |
Caching
Geocoding results are automatically cached (up to 10,000 entries) to minimize API calls and improve performance. The cache persists for the lifetime of the Structr process.
Working with Geometries
For more complex geographic data like polygons, boundaries, or routes, create a custom Geometry type that stores WKT (Well-Known Text) representations.
Creating a Geometry Type
In the Schema area, create a type with these properties:
| Property | Type | Description |
|---|---|---|
wkt |
String | WKT representation of the geometry |
name |
String | Name or identifier |
Add a schema method getGeometry to convert WKT to a geometry object:
// Schema method: getGeometry
{
return $.wktToGeometry($.this.wkt);
}
Add a method contains to check if a point is inside:
// Schema method: contains (parameter: point)
{
let point = $.retrieve('point');
let geometry = $.this.getGeometry();
let pointGeom = $.wktToGeometry('POINT(' + point.latitude + ' ' + point.longitude + ')');
return geometry.contains(pointGeom);
}
Creating Geometries
{
// Create a polygon
let polygon = $.create('Geometry', {
name: 'Delivery Zone A',
wkt: 'POLYGON ((8.6 50.0, 8.8 50.0, 8.8 50.2, 8.6 50.2, 8.6 50.0))'
});
// Create a line
let route = $.create('Geometry', {
name: 'Route 1',
wkt: 'LINESTRING (8.68 50.11, 8.69 50.12, 8.70 50.13)'
});
}
Point-in-Polygon Queries
Check if a point is inside a geometry:
{
let point = { latitude: 50.1, longitude: 8.7 };
// Check against a single geometry
let zone = $.first($.find('Geometry', 'name', 'Delivery Zone A'));
if (zone.contains(point)) {
$.log('Point is inside delivery zone');
}
// Find all geometries containing a point
let geometries = $.find('Geometry');
let matching = [];
for (let geom of geometries) {
if (geom.contains(point)) {
matching.push(geom);
}
}
}
Geometry Functions
Structr provides functions for creating, parsing, and analyzing geometries.
Creating Geometries
| Function | Description |
|---|---|
coordsToPoint(coord) |
Create Point from [x, y], {x, y}, or {latitude, longitude} |
coordsToLineString(coords) |
Create LineString from array of coordinates |
coordsToPolygon(coords) |
Create Polygon from array of coordinates |
coordsToMultipoint(coords) |
Create MultiPoint from array of coordinates |
{
let point = $.coordsToPoint([8.6821, 50.1109]);
let point2 = $.coordsToPoint({ latitude: 50.1109, longitude: 8.6821 });
let line = $.coordsToLineString([[8.68, 50.11], [8.69, 50.12], [8.70, 50.13]]);
let polygon = $.coordsToPolygon([
[8.6, 50.0], [8.8, 50.0], [8.8, 50.2], [8.6, 50.2], [8.6, 50.0]
]);
}
Parsing Geometries
| Function | Description |
|---|---|
wktToGeometry(wkt) |
Parse WKT string to geometry |
wktToPolygons(wkt) |
Extract all polygons from WKT |
{
let point = $.wktToGeometry('POINT (8.6821 50.1109)');
let polygon = $.wktToGeometry('POLYGON ((8.6 50.0, 8.8 50.0, 8.8 50.2, 8.6 50.2, 8.6 50.0))');
}
Calculations
| Function | Description |
|---|---|
distance(point1, point2) |
Geodetic distance in meters |
azimuth(point1, point2) |
Bearing in degrees |
getCoordinates(geometry) |
Extract coordinates as array |
{
let frankfurt = $.coordsToPoint([8.6821, 50.1109]);
let berlin = $.coordsToPoint([13.405, 52.52]);
let distanceMeters = $.distance(frankfurt, berlin);
$.log('Distance: ' + (distanceMeters / 1000).toFixed(1) + ' km');
let bearing = $.azimuth(frankfurt, berlin);
$.log('Bearing: ' + bearing.toFixed(1) + '°');
}
Coordinate Conversion
| Function | Description |
|---|---|
latLonToUtm(lat, lon) |
Convert to UTM string |
utmToLatLon(utmString) |
Convert UTM to lat/lon object |
convertGeometry(srcCRS, dstCRS, geom) |
Transform coordinate system |
{
// Lat/Lon to UTM
let utm = $.latLonToUtm(53.855, 8.0817);
// Result: "32U 439596 5967780"
// UTM to Lat/Lon
let coords = $.utmToLatLon('32U 439596 5967780');
// Result: { latitude: 53.855, longitude: 8.0817 }
// Transform between coordinate systems
let wgs84Point = $.wktToGeometry('POINT (8.6821 50.1109)');
let utmPoint = $.convertGeometry('EPSG:4326', 'EPSG:32632', wgs84Point);
}
File Import
GPX Import
The importGpx function parses GPS track files:
{
let file = $.first($.find('File', 'name', 'track.gpx'));
let gpxData = $.importGpx($.getContent(file, 'utf-8'));
// Process waypoints
if (gpxData.waypoints) {
for (let wp of gpxData.waypoints) {
$.create('Waypoint', {
name: wp.name,
latitude: wp.latitude,
longitude: wp.longitude,
altitude: wp.altitude
});
}
}
// Process tracks
if (gpxData.tracks) {
for (let track of gpxData.tracks) {
let points = [];
for (let segment of track.segments) {
for (let point of segment.points) {
points.push([point.longitude, point.latitude]);
}
}
$.create('Route', {
name: track.name,
wkt: $.coordsToLineString(points).toString()
});
}
}
}
Shapefile Import
The readShapefile function reads ESRI Shapefiles:
{
let result = $.readShapefile('/data/regions.shp');
$.log('Fields: ' + result.fields.join(', '));
for (let item of result.geometries) {
$.create('Region', {
name: item.metadata.NAME,
wkt: item.wkt,
population: item.metadata.POPULATION
});
}
}
The function automatically reads the associated .dbf file for attributes and .prj file for coordinate reference system, transforming coordinates to WGS84.
Map Layers
For applications with multiple geometry sources (e.g., different Shapefiles), organize geometries into layers:
{
// Create a map layer
let layer = $.create('MapLayer', {
name: 'Administrative Boundaries',
description: 'Country and state boundaries'
});
// Import shapefile into layer
let result = $.readShapefile('/data/boundaries.shp');
for (let item of result.geometries) {
$.create('Geometry', {
mapLayer: layer,
name: item.metadata.NAME,
wkt: item.wkt
});
}
}
Examples
Store Locator
// Schema method on Store: findNearby (parameters: latitude, longitude, radiusKm)
{
let lat = $.retrieve('latitude');
let lon = $.retrieve('longitude');
let radius = $.retrieve('radiusKm');
let stores = $.find('Store', $.withinDistance(lat, lon, radius));
let customerPoint = $.coordsToPoint([lon, lat]);
let result = [];
for (let store of stores) {
let storePoint = $.coordsToPoint([store.longitude, store.latitude]);
let dist = $.distance(customerPoint, storePoint);
result.push({
store: store,
distanceKm: (dist / 1000).toFixed(1)
});
}
// Sort by distance
result.sort((a, b) => a.distanceKm - b.distanceKm);
return result;
}
Geofencing
// Global schema method: checkDeliveryZone (parameters: latitude, longitude)
{
let lat = $.retrieve('latitude');
let lon = $.retrieve('longitude');
let point = $.wktToGeometry('POINT(' + lat + ' ' + lon + ')');
let zones = $.find('DeliveryZone');
for (let zone of zones) {
let polygon = $.wktToGeometry(zone.wkt);
if (polygon.contains(point)) {
return {
inZone: true,
zoneName: zone.name,
deliveryFee: zone.deliveryFee
};
}
}
return { inZone: false };
}
Related Topics
- Building a Spatial Index - Tutorial for optimizing point-in-polygon queries
- REST API - Distance queries with
_latlon,_distance, and address parameters - Schema - Creating custom types for geographic data
- Scheduled Tasks - Batch geocoding and index building